#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File     : py_douban.py
# @Author   : jade
# @Date     : 2023/11/24 10:46
# @Email    : jadehh@1ive.com
# @Software : Samples
# @Desc     :
import base64
import requests
import time
import os
import logging
import logging.config
import json
import copy
import sys
from abc import abstractmethod, ABCMeta
from importlib.machinery import SourceFileLoader
import urllib3
from urllib3.exceptions import InsecureRequestWarning
import xml.etree.ElementTree as ET

urllib3.disable_warnings(InsecureRequestWarning)
LocalAddress = "https://gh.con.sh/https://raw.githubusercontent.com/jadehh/TV/py"


class VodShort(object):
    def __init__(self):
        self.vod_id = ""  ## id
        self.vod_name = ""  ## 名称
        self.vod_pic = ""  ## 图片
        self.vod_remarks = ""  ## 备注

    def to_dict(self):
        dic = {}
        for item in self.__dict__.items():
            dic[item[0]] = item[1]
        return dic

    def set_id_to_dic(self):
        dic = self.to_dict()
        new_dic = copy.copy(dic)
        new_dic["vod_id"] = json.dumps(dic)
        return new_dic

    def load_dic(self, dic):
        for key in list(dic.keys()):
            if key in list(self.to_dict().keys()):
                setattr(self, key, dic[key])


class VodDetail(VodShort):
    def __init__(self):
        super().__init__()
        self.type_name = ""  ## 类别
        self.vod_year = ""  ## 年份
        self.vod_area = ""  ## 地区
        self.vod_actor = ""  ## 导演
        self.vod_director = ""  ## 演员
        self.vod_content = ""  ## 剧情
        self.vod_play_from = ""  ## 播放格式
        self.vod_play_url = ""  ## 播放连接
        self.vod_pic = ""  ## 封面

    def to_short(self):
        vodShort = VodShort()
        vodShort.load_dic(self.to_dict())
        return vodShort.to_dict()


class Logger(object):
    def __init__(self, name):
        self.name = name
        logging.getLogger("urllib3.connectionpool").setLevel(logging.ERROR)
        LOGGING_CONFIG = {
            'version': 1,
            'disable_existing_loggers': False,
            'formatters': {
                'default': {
                    'format': '%(asctime)s - %(name)s - %(levelname)s: %(message)s',
                }
            },
            'handlers': {
                'console': {
                    'level': 'DEBUG',
                    'class': 'logging.StreamHandler',
                    'formatter': 'default',
                },
                'file': {
                    'level': 'INFO',
                    'class': 'logging.FileHandler',
                    'filename': os.path.join(os.environ.get("HOME"), "info.log"),
                    'formatter': 'default',
                }
            },
            'root': {
                'level': 'DEBUG',
                'handlers': ['console', 'file'],
            }
        }
        logging.config.dictConfig(LOGGING_CONFIG)

    def get_logger(self):
        return logging.getLogger(self.name)


class BaseSpider(metaclass=ABCMeta):
    _instance = None
    tree = None
    header = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"}
    session = requests.session()
    search_index = 0
    index = 1
    _user_agents = [
        "api-client/1 com.douban.frodo/7.22.0.beta9(231) Android/23 product/Mate 40 vendor/HUAWEI model/Mate 40 brand/HUAWEI  rom/android  network/wifi  platform/AndroidPad"
        "api-client/1 com.douban.frodo/7.18.0(230) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android  rom/miui6  network/wifi  platform/mobile nd/1",
        "api-client/1 com.douban.frodo/7.1.0(205) Android/29 product/perseus vendor/Xiaomi model/Mi MIX 3  rom/miui6  network/wifi  platform/mobile nd/1",
        "api-client/1 com.douban.frodo/7.3.0(207) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android  rom/miui6  network/wifi platform/mobile nd/1"]
    vod_douban_detail = None

    def __new__(cls, *args, **kwargs):
        if cls._instance:
            return cls._instance
        else:
            cls._instance = super().__new__(cls)
            return cls._instance

    @abstractmethod
    def init(self, extend=""):
        pass

    @abstractmethod
    def homeContent(self, filter):
        pass

    @abstractmethod
    def homeVideoContent(self):
        pass

    @abstractmethod
    def categoryContent(self, tid, pg, filter, extend):
        pass

    @abstractmethod
    def liveContent(self, url):
        pass

    @abstractmethod
    def detailContent(self, ids):
        pass

    @abstractmethod
    def searchContent(self, xxx, key, quick):
        pass

    @abstractmethod
    def playerContent(self, flag, id, vipFlags):
        pass

    @abstractmethod
    def localProxy(self, param):
        pass

    @abstractmethod
    def isVideoFormat(self, url):
        pass

    @abstractmethod
    def manualVideoCheck(self):
        pass

    @abstractmethod
    def getName(self):
        pass

    def get_name(self):
        name = self.getName().split('┃')[1]
        return name

    def init_logger(self):
        # self.clear()
        self.logger = Logger(self.get_name()).get_logger()
        self.logger.info("##################{}爬虫脚本初始化完成##################".format(self.get_name()))

    def num2cn(self, num):
        """
        将阿拉伯数字转换为中文数字
        :param num: 阿拉伯数字
        :return: 中文数字
        """
        cn_num = {
            "0": '零',
            "1": '一',
            "2": '二',
            "3": '三',
            "4": '四',
            "5": '五',
            "6": '六',
            "7": '七',
            "8": '八',
            "9": '九'
        }
        new_str = ""
        status = False
        for i in num:
            if i in cn_num.keys():
                new_str = new_str + cn_num[i]
                status = True
            else:
                new_str = new_str + i
        return status, new_str

    def xml_to_dict(self, element):
        result = {}
        if element.attrib:
            result['@attributes'] = element.attrib
        if element.text and element.text.strip():
            result['#text'] = element.text.strip()
        for child in element:
            child_name = child.tag
            if '}' in child_name:
                child_name = child_name.split('}', 1)[1]

            child_dict = self.xml_to_dict(child)
            if child_name in result:
                if isinstance(result[child_name], list):
                    result[child_name].append(child_dict)
                else:
                    result[child_name] = [result[child_name], child_dict]
            else:
                result[child_name] = child_dict
        return result

    def write_config(self, dic, name):
        if os.path.exists(os.path.join(os.environ.get("HOME"))):
            pass
        else:
            os.mkdir(os.path.join(os.environ.get("HOME")))
        with open(os.path.join(os.environ.get("HOME"), "{}.json".format(name)), "wb") as f:
            f.write(json.dumps(dic, indent=4, ensure_ascii=False).encode("utf-8"))
        return dic

    def load_config(self, name):
        try:
            with open(os.path.join(os.environ.get("HOME"), "{}.json".format(name)), "rb") as f:
                return json.load(f)
        except:
            return {}

    def fetch(self, url, header=None):
        try:
            if header:
                rsp = self.session.get(url, headers=self.header)
            else:
                rsp = self.session.get(url, headers=header)
            return rsp
        except Exception as e:
            sys.exit()
            return None

    def getDependence(self):
        return []

    def clear(self):
        try:
            file_list = os.listdir(os.environ.get("HOME"))
            for file_name in file_list:
                if os.path.isfile(os.path.join(os.environ.get("HOME"), file_name)):
                    os.remove(os.path.join(os.environ.get("HOME"), file_name))
        except Exception as e:
            pass

    def loadModule(self, name, fileName):
        return SourceFileLoader(name, fileName).load_module()


class Spider(BaseSpider):
    header = {"Host": "frodo.douban.com",
              "Connection": "Keep-Alive",
              "Referer": "https://servicewechat.com/wx2f9b06c1de1ccfca/84/page-frame.html",
              "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat"
              }
    cache = {}
    session = requests.session()

    page_url = "https://inv2.nadeko.net"

    def liveContent(self, url):
        url = "https://raw.githubusercontent.com/TVGarden/tv-garden-channel-list/main/channels/compressed/categories/all-channels.json"
        
        return json.dumps([{
            "name": "新闻",
            "channel": [{"name": "23 ABC Bakersfield CA (KERO)",
                         "urls": ["https://content.uplynk.com/channel/ff809e6d9ec34109abfb333f0d4444b5.m3u8"]},
                        {"name": "30A Darcizzle Offshore", "urls": ["https://30a-tv.com/darcizzle.m3u8"]},
                        {"name": "30A Georgia Hollywood Review TV", "urls": ["https://30a-tv.com/gh.m3u8"]},
                        {"name": "30A Golf Kingdom", "urls": ["https://30a-tv.com/feeds/vidaa/golf.m3u8"]},
                        {"name": "30A Loomered TV", "urls": ["https://30a-tv.com/loomer.m3u8"]},
                        {"name": "30A Ridiculous TV", "urls": ["https://30a-tv.com/feeds/720p/63.m3u8"]},
                        {"name": "30A The Beach Show", "urls": ["https://30a-tv.com/beachy.m3u8"]},
                        {"name": "30A TV Classic Movies", "urls": ["https://30a-tv.com/feeds/pzaz/30atvmovies.m3u8"]},
                        {"name": "30A We Love Cars", "urls": ["https://30a-tv.com/feeds/vidaa/cars.m3u8"]},
                        {"name": "ABC 4 Seattle WA (KOMO-TV)",
                         "urls": ["https://content.uplynk.com/2c88dfe19e1447e6a6aa27e8e143a140.m3u8"]},
                        {"name": "ABC 7 Bay Area and San Francisco News (KGO-DT1)", "urls": [
                            "https://content.uplynk.com/channel/ext/4413701bf5a1488db55b767f8ae9d4fa/kgo_24x7_news.m3u8"]},
                        {"name": "ABC 7 Denver CO (KMGH)",
                         "urls": ["https://content.uplynk.com/channel/64ca339f04964a5e961c3207a7771bf1.m3u8"]},
                        {"name": "ABC 7 Los Angeles CA (KABC-TV)", "urls": [
                            "https://content.uplynk.com/channel/ext/2118d9222a87420ab69223af9cfa0a0f/kabc_24x7_news.m3u8"]},
                        {"name": "ABC 13 Las Vegas NV (KTNV)",
                         "urls": ["https://content.uplynk.com/channel/39919d3f7a074eefa8bf579214e952f9.m3u8"]},
                        {"name": "ABC 15 Phoenix AZ (KNXV-TV)",
                         "urls": ["https://content.uplynk.com/channel/9deaf22aaa33461f9cac22e030ed00ec.m3u8"]},
                        {"name": "ABC 30 Fresno and Central Valley News (KFSN-DT1)", "urls": [
                            "https://content.uplynk.com/channel/ext/96195dc445894d079a91958abba8d3af/kfsn_24x7_news.m3u8"]},
                        {"name": "ABC News New York NY (WABC-TV)", "urls": [
                            "https://content.uplynk.com/channel/ext/72750b711f704e4a94b5cfe6dc99f5e1/wabc_24x7_news.m3u8"]},
                        {"name": "ABN Bible Movies",
                         "urls": ["https://mediaserver.abnvideos.com/streams/abnbiblemovies.m3u8"]}, {"name": "ACCDN",
                                                                                                      "urls": [
                                                                                                          "https://aegis-cloudfront-1.tubi.video/ce6650c2-26ea-4433-9276-8a5ebf3992cc/playlist.m3u8"]},
                        {"name": "Action (KCKS-LD4)",
                         "urls": ["https://cdn.igocast.com/channel4_hls/channel4_master.m3u8"]},
                        {"name": "AfroLandTV", "urls": ["https://alt-al.otteravision.com/alt/al/al.m3u8"]},
                        {"name": "Akaku 54 (Hawaii)",
                         "urls": ["https://castus-vod-dev.s3.amazonaws.com/vod_clients/akaku/live/ch2/video.m3u8"]},
                        {"name": "Akaku 55 (Hawaii)",
                         "urls": ["https://castus-vod-dev.s3.amazonaws.com/vod_clients/akaku/live/ch3/video.m3u8"]},
                        {"name": "AKC TV",
                         "urls": ["https://broadcast.blivenyc.com/speed/broadcast/22/desktop-playlist.m3u8",
                                  "https://install.akctvcontrol.com/speed/broadcast/138/desktop-playlist.m3u8"]},
                        {"name": "AKC TV Meet The Breeds",
                         "urls": ["https://install.akctvcontrol.com/speed/broadcast/139/desktop-playlist.m3u8"]},
                        {"name": "AKC TV Puppies 24/7",
                         "urls": ["https://install.akctvcontrol.com/speed/broadcast/140/desktop-playlist.m3u8"]},
                        {"name": "Al Jazeera English", "urls": ["https://live-hls-v3-aje.getaj.net/AJE-V3/index.m3u8"]},
                        {"name": "Always Funny Videos",
                         "urls": ["https://linear-12.frequency.stream/dist/glewedtv/12/hls/master/playlist.m3u8"]},
                        {"name": "At Home with Family Handyman",
                         "urls": ["https://linear-458.frequency.stream/dist/xumo/458/hls/master/playlist.m3u8"]},
                        {"name": "AXS TV NOW",
                         "urls": ["https://dikcfc9915kp8.cloudfront.net/hls/1080p/playlist.m3u8"]}, {"name": "BBC News",
                                                                                                     "urls": [
                                                                                                         "https://vs-hls-push-ww-live.akamaized.net/x=4/i=urn:bbc:pips:service:bbc_news_channel_hd/mobile_wifi_main_sd_abr_v2.m3u8"]},
                        {"name": "BBC News Asia Pacific",
                         "urls": ["https://cdn4.skygo.mn/live/disk1/BBC_News/HLSv3-FTA/BBC_News.m3u8"]},
                        {"name": "Beverly Hills Television 35",
                         "urls": ["https://cdn3.wowza.com/5/V2Y2VmhqMEFDTUkx/beverlyhills/G0072_007/playlist.m3u8"]},
                        {"name": "Bolton Community Voice Channel (CVC) Education (Bolton CT)",
                         "urls": ["https://s3-us-west-2.amazonaws.com/bolton.castus-vod/live/ch2/video.m3u8"]},
                        {"name": "Bolton Community Voice Channel (CVC) Government (Bolton CT)",
                         "urls": ["https://s3-us-west-2.amazonaws.com/bolton.castus-vod/live/ch3/video.m3u8"]},
                        {"name": "Bolton Community Voice Channel (CVC) Public (Bolton CT)",
                         "urls": ["https://s3-us-west-2.amazonaws.com/bolton.castus-vod/live/ch1/video.m3u8"]},
                        {"name": "BYU TV",
                         "urls": ["https://content.uplynk.com/channel/2f29cb20fcb74f68a85c38b99ca384bc.m3u8"]},
                        {"name": "CampusLore",
                         "urls": ["https://linear-235.frequency.stream/dist/glewedtv/235/hls/master/playlist.m3u8"]},
                        {"name": "CAPS Media Channel 15 (Ventura CA)",
                         "urls": ["https://reflect-stream15-capsmedia.cablecast.tv/live/live.m3u8"]},
                        {"name": "Caught in Providence",
                         "urls": ["https://livetv-fa.tubi.video/caught-in-providence2/playlist.m3u8"]},
                        {"name": "CBN News National", "urls": [
                            "https://bcovlive-a.akamaihd.net/re8d9f611ee4a490a9bb59e52db91414d/us-east-1/734546207001/playlist.m3u8"]},
                        {"name": "CBS News Baltimore", "urls": [
                            "https://lineup.cbsivideo.com/playout/8877a7d5-b094-4ac8-bc72-75a5fed68e5b/index.m3u8"]},
                        {"name": "CBS News Bay Area", "urls": [
                            "https://cbsn-sf.cbsnstream.cbsnews.com/out/v1/dac63c1abb3f4a2dac9f508f44bb072a/master.m3u8"]},
                        {"name": "CBS News Detroit", "urls": [
                            "https://cbsn-det.cbsnstream.cbsnews.com/out/v1/169f5c001bc74fa7a179b19c20fea069/master.m3u8"]},
                        {"name": "CBS News Los Angeles", "urls": [
                            "https://cbsn-la.cbsnstream.cbsnews.com/out/v1/57b6c4534a164accb6b1872b501e0028/master.m3u8"]},
                        {"name": "CBS News Miami", "urls": [
                            "https://cbsn-mia.cbsnstream.cbsnews.com/out/v1/ac174b7938264d24ae27e56f6584bca0/master.m3u8",
                            "https://lineup.cbsivideo.com/playout/8796731e-7ce7-4bcf-bb04-41ef5a969c62/master.m3u8"]},
                        {"name": "CBS News Minnesota", "urls": [
                            "https://cbsn-min.cbsnstream.cbsnews.com/out/v1/76518f06941246ba810c8d175600bf74/master.m3u8"]},
                        {"name": "CBS News Philadelphia", "urls": [
                            "https://cbsn-phi.cbsnstream.cbsnews.com/out/v1/5c9ad3e215984b0e9ad845b335216b72/master.m3u8"]},
                        {"name": "CBS News Sacramento", "urls": [
                            "https://lineup.cbsivideo.com/playout/c1ed69db-6b71-4581-a937-a70ab4089f8a/index.m3u8"]},
                        {"name": "Celebrity Scene TV",
                         "urls": ["https://bozztv.com/gusa/gusa-celebrityscene/index.m3u8"]},
                        {"name": "CFM TV Channel 98",
                         "urls": ["https://cdn3.wowza.com/5/djRwZmQvTEJidmZD/cityftmyers/G2083_002/playlist.m3u8"]},
                        {"name": "CGTN", "urls": ["https://english-livebkws.cgtn.com/live/encgtn.m3u8"]},
                        {"name": "CGTN Documentary", "urls": ["https://english-livebkali.cgtn.com/live/doccgtn.m3u8"]},
                        {"name": "Charlotte County CC-TV (Charlotte County FA)",
                         "urls": ["https://cdn3.wowza.com/5/cHYzekYzM2kvTVFH/charlotte/G0055_009/playlist.m3u8"]},
                        {"name": "Chef Champion", "urls": ["https://bozztv.com/gusa/gusa-chefchampion/index.m3u8"]},
                        {"name": "Chef Roc Show", "urls": ["https://bozztv.com/gusa/gusa-chefrock/index.m3u8"]},
                        {"name": "Cine Romantico",
                         "urls": ["https://stream.ads.ottera.tv/playlist.m3u8?network_id=656"]},
                        {"name": "City of Oakland (KTOP)",
                         "urls": ["https://cdn3.wowza.com/5/cHYzekYzM2kvTVFH/oakland/G0219_002/playlist.m3u8"]},
                        {"name": "CityNews Calgary", "urls": [
                            "https://citynewsregional.akamaized.net/hls/live/1024053/Regional_Live_8/master.m3u8"]},
                        {"name": "CityNews Toronto", "urls": [
                            "https://citynewsregional.akamaized.net/hls/live/1024052/Regional_Live_7/master.m3u8"]},
                        {"name": "CityNews Vancouver", "urls": [
                            "https://citynewsregional.akamaized.net/hls/live/1024054/Regional_Live_9/master.m3u8"]},
                        {"name": "CityTV San Diego",
                         "urls": ["https://cdn3.wowza.com/5/dk84U1p2UUdoMGxT/sandiego/G1826_005/playlist.m3u8"]},
                        {"name": "Classic Cinema", "urls": ["https://bozztv.com/gusa/gusa-classiccinema/index.m3u8"]},
                        {"name": "CNA International",
                         "urls": ["https://d2e1asnsl7br7b.cloudfront.net/7782e205e72f43aeb4a48ec97f66ebbe/index.m3u8"]},
                        {"name": "Comedy Dynamics", "urls": [
                            "https://aegis-cloudfront-1.tubi.video/427cd05d-a47b-4b16-a2fe-5e739da93966/playlist.m3u8"]},
                        {"name": "Comet",
                         "urls": ["https://content.uplynk.com/channel/3e45c6b5354a40f787e0b2aadb0f5d6a.m3u8"]},
                        {"name": "Coral Gables Television (Coral Gables FL)",
                         "urls": ["https://cdn3.wowza.com/5/R09KQXpaMWlrRjly/coralgables/G0937_004/playlist.m3u8"]},
                        {"name": "Court TV",
                         "urls": ["https://content.uplynk.com/channel/6c0bd0f94b1d4526a98676e9699a10ef.m3u8"]},
                        {"name": "Deluxe Rap", "urls": [
                            "https://sdn-global-live-streaming-packager-cache.3qsdn.com/65183/65183_264_live.m3u8"]},
                        {"name": "Dr. Gene Scott",
                         "urls": ["https://wescottcc.piksel.tech/Manifest/UTB-NetworkStream.m3u8"]},
                        {"name": "Electric Now", "urls": ["https://elec-en.otteravision.com/elec/en/elec_en.m3u8",
                                                          "https://elec-en-xumo.otteravision.com/elec/en/elec_en.m3u8"]},
                        {"name": "EWTN Asia-Pacific",
                         "urls": ["https://cdn3.wowza.com/1/QmVNUVhTNTZSS3Uz/YWQ0aHpi/hls/live/playlist.m3u8"]},
                        {"name": "EWTN United States",
                         "urls": ["https://cdn3.wowza.com/1/QjRzVXJtVml5SUVx/WGJRSWxz/hls/live/playlist.m3u8"]},
                        {"name": "Fame95 FM",
                         "urls": ["https://rjr-fame.akamaized.net/hls/live/2033820/RJR_FAME/master.m3u8"]},
                        {"name": "Fashion TV (Spain)",
                         "urls": ["https://fashiontv-fashiontv-5-nl.samsung.wurl.tv/playlist.m3u8"]},
                        {"name": "Fashion TV UHD", "urls": [
                            "https://fash2043.cloudycdn.services/slive/ftv_ftv_4k_hevc_73d_42080_default_466_hls.smil/playlist.m3u8"]},
                        {"name": "Fort Pierce Live Stream (Fort Pierce FL)",
                         "urls": ["https://edge-f.swagit.com/live/fortpiercefl/live-1-a/playlist.m3u8"]},
                        {"name": "FOX 61 Hartford CT (WTIC)", "urls": ["https://livetv-fa.tubi.video/wtic/live.m3u8"]},
                        {"name": "FOX News Now", "urls": ["https://fox-foxnewsnow-samsungus.amagi.tv/playlist.m3u8"]},
                        {"name": "France 24 English",
                         "urls": ["https://live.france24.com/hls/live/2037218/F24_EN_HI_HLS/master_5000.m3u8",
                                  "https://live.france24.com/hls/live/2037218/F24_EN_HI_HLS/master_2300.m3u8",
                                  "https://live.france24.com/hls/live/2037218/F24_EN_HI_HLS/master_900.m3u8",
                                  "https://live.france24.com/hls/live/2037218/F24_EN_HI_HLS/master_500.m3u8"]},
                        {"name": "GS Shop",
                         "urls": ["https://gstv-gsshop.gsshop.com/gsshop_hd/gsshop_hd.stream/playlist.m3u8"]},
                        {"name": "Haunt TV", "urls": [
                            "https://aegis-cloudfront-1.tubi.video/43911d88-1ba4-4595-9b35-bf96d09efeef/playlist.m3u8"]},
                        {"name": "HSN",
                         "urls": ["https://qvc-amd-live.akamaized.net/hls/live/2034113/lshsn1us/master.m3u8"]},
                        {"name": "HSN West",
                         "urls": ["https://qvc-amd-live.akamaized.net/hls/live/2034113/lshsn1uswest/master.m3u8"]},
                        {"name": "HSN2",
                         "urls": ["https://qvc-amd-live.akamaized.net/hls/live/2034113/lshsn2us/master.m3u8"]},
                        {"name": "HTV 2 Houston Television",
                         "urls": ["https://stream.swagit.com/live-edge/houstontx/smil:hd-16x9-2-b/playlist.m3u8"]},
                        {"name": "In the kitchen",
                         "urls": ["https://qvc-amd-live.akamaized.net/hls/live/2034113/lsqvc4us/master.m3u8"]},
                        {"name": "It's Supernatural! Network (ISN Network)",
                         "urls": ["https://content.uplynk.com/channel/fbc0f835332e476397b12216f9042f78.m3u8"]},
                        {"name": "Joshua TV", "urls": ["https://livecdn.live247stream.com/joshua/tv/playlist.m3u8"]},
                        {"name": "KARE-DT1", "urls": ["https://livetv-fa.tubi.video/kare/live.m3u8"]},
                        {"name": "KBEV Spectrum Channel 6 [Beverly Hills]", "urls": [
                            "https://s3-us-west-2.amazonaws.com/beverly-hills-high-school.castus-vod/live/ch1/video.m3u8"]},
                        {"name": "KGW-DT1", "urls": ["https://livetv-fa.tubi.video/kgw/live.m3u8"]},
                        {"name": "KPNX-DT1", "urls": ["https://livetv-fa.tubi.video/kpnx/live.m3u8"]},
                        {"name": "KRCA-TV Estrella Los Angeles",
                         "urls": ["https://content.uplynk.com/channel/829a8c85f9e840c6bed963856c231428.m3u8"]},
                        {"name": "KRIS-DT1",
                         "urls": ["https://content.uplynk.com/channel/e72660594b7a4182982af1690609683d.m3u8",
                                  "https://content.uplynk.com/channel/f805c6ab7ddf4e2489ae648bb28fb133.m3u8"]},
                        {"name": "KSBY News San Louis Obispo CA",
                         "urls": ["https://content.uplynk.com/channel/5545aa8a7e5a480d85fd1cff8d0cf610.m3u8"]},
                        {"name": "KSHB 41 (Kansas City MO)",
                         "urls": ["https://content.uplynk.com/channel/50d0fa1b042945a3a4f550f9b8412c83.m3u8"]},
                        {"name": "KTRK TV ABC 13 News", "urls": [
                            "https://content.uplynk.com/channel/ext/1efe3bfc4d1e4b5db5e5085a535b510b/ktrk_24x7_news.m3u8"]},
                        {"name": "KUSA-DT1", "urls": ["https://livevideo01.9news.com/hls/live/2014548/elvs/live.m3u8",
                                                      "https://livetv-fa.tubi.video/kusa/live.m3u8"]},
                        {"name": "KZTV-DT1",
                         "urls": ["https://content.uplynk.com/channel/d1f868588ae5476dadaeff450170a183.m3u8"]},
                        {"name": "Lakewood CityTV",
                         "urls": ["https://s3-us-west-2.amazonaws.com/lakewood.castus-vod/live/ch1/video.m3u8"]},
                        {"name": "Lolwe TV", "urls": ["https://goliveafrica.media:9998/live/62580e144eb43/index.m3u8"]},
                        {"name": "Mad Dog and Merrill", "urls": ["https://bozztv.com/gusa/gusa-mwg/index.m3u8"]},
                        {"name": "Midpen Media Center Diversity and Culture Channel 75",
                         "urls": ["https://5c2974786200d.streamlock.net/live-chan75/ngrp:ch75_all/playlist.m3u8"]},
                        {"name": "MNB World", "urls": ["https://live.mnb.mn/hls/mnb_world.stream.m3u8"]},
                        {"name": "Motorvision", "urls": ["https://mvg-mv-xumo.otteravision.com/mvg/mv/mv.m3u8"]},
                        {"name": "MUTV", "urls": [
                            "https://bcovlive-a.akamaihd.net/r2d2c4ca5bf57456fb1d16255c1a535c8/eu-west-1/6058004203001/playlist.m3u8"]},
                        {"name": "Muzzik POP STAR",
                         "urls": ["https://muzzik-live.morescreens.com/mts-3/playlist.m3u8"]},
                        {"name": "Muzzik SENSE", "urls": ["https://muzzik-live.morescreens.com/mts-7/playlist.m3u8"]},
                        {"name": "Muzzik ZZ TV", "urls": ["https://muzzik-live.morescreens.com/mts-a4/playlist.m3u8"]},
                        {"name": "MyTime Movie Network",
                         "urls": ["https://livetv-fa.tubi.video/my-time/playlist.m3u8"]}, {"name": "NBA", "urls": [
                    "https://aegis-cloudfront-1.tubi.video/c987b155-8d7e-4307-bd24-720bb2843d42/playlist.m3u8"]},
                        {"name": "NBC 5 Seattle WA (KING-DT1)",
                         "urls": ["https://livetv-fa.tubi.video/king/live.m3u8"]}, {"name": "News 12 New York",
                                                                                    "urls": [
                                                                                        "https://aegis-cloudfront-1.tubi.video/1568cc70-2bf5-4f52-805b-42e0e514a77a/playlist.m3u8"]},
                        {"name": "Newsy",
                         "urls": ["https://content.uplynk.com/channel/1f93c13275024afb9e0ead299624073d.m3u8",
                                  "https://content.uplynk.com/channel/4bb4901b934c4e029fd4c1abfc766c37.m3u8"]},
                        {"name": "Nosey", "urls": [
                            "https://aegis-cloudfront-1.tubi.video/78c30bbd-f60c-4ee6-9922-ad7aa63ca8ec/playlist.m3u8"]},
                        {"name": "Olelo Community Media Channel 53",
                         "urls": ["https://cdn3.wowza.com/5/V2Y2VmhqMEFDTUkx/olelo/G0125_011/playlist.m3u8"]},
                        {"name": "Ontario Public Access Channel",
                         "urls": ["https://cdn3.wowza.com/5/dk84U1p2UUdoMGxT/ontarioca/G2446_002/playlist.m3u8"]},
                        {"name": "Poker Go",
                         "urls": ["https://linear-426.frequency.stream/dist/xumo/426/hls/master/playlist.m3u8"]},
                        {"name": "Pursuit UP",
                         "urls": ["https://linear-205.frequency.stream/dist/xumo/205/hls/master/playlist.m3u8"]},
                        {"name": "QTV Gambia", "urls": ["https://player.qtv.gm/hls/live.stream.m3u8"]},
                        {"name": "QVC 3",
                         "urls": ["https://qvc-amd-live.akamaized.net/hls/live/2034113/lsqvc3us/master.m3u8"]},
                        {"name": "QVC Live",
                         "urls": ["https://qvc-amd-live.akamaized.net/hls/live/2034113/lsqvc1us/master.m3u8"]},
                        {"name": "Reelz", "urls": [
                            "https://bcovlive-a.akamaihd.net/rc21a8bc6455344c699a06e9220fe2748/us-west-2/5245389775001/playlist.m3u8"]},
                        {"name": "Retro TV (KCKS-LD9)",
                         "urls": ["https://cdn.igocast.com/channel9_hls/channel9_master.m3u8"]},
                        {"name": "Revolt", "urls": ["https://livetv-fa.tubi.video/revolt/playlist.m3u8"]},
                        {"name": "Revry 2",
                         "urls": ["https://linear-5.frequency.stream/mt/brightcove/5/hls/master/playlist.m3u8"]},
                        {"name": "Revry Her",
                         "urls": ["https://linear-73.frequency.stream/mt/brightcove/73/hls/master/playlist.m3u8"]},
                        {"name": "Scripps News", "urls": ["https://livetv-fa.tubi.video/scripps-news/playlist.m3u8"]},
                        {"name": "SF Commons Channel 76 (San Francisco CA)",
                         "urls": ["https://reflect-bayarea.cablecast.tv/live-6/live/live.m3u8"]},
                        {"name": "Shop LC (KCKS-LD10)",
                         "urls": ["https://cdn.igocast.com/channel10_hls/channel10_master.m3u8"]},
                        {"name": "SLO County Channel 21",
                         "urls": ["https://agp-nimble.streamguys1.com/SLOC/SLOC/playlist.m3u8"]},
                        {"name": "Stingray Classica", "urls": [
                            "https://lotus.stingray.com/manifest/classica-cla008-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray CMusic", "urls": [
                            "https://lotus.stingray.com/manifest/cmusic-cme004-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray DJAZZ", "urls": [
                            "https://lotus.stingray.com/manifest/djazz-djaads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Easy Listening",
                         "urls": ["https://lotus.stingray.com/manifest/ose-137ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Flashback 70s",
                         "urls": ["https://lotus.stingray.com/manifest/ose-115ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Hit List",
                         "urls": ["https://lotus.stingray.com/manifest/ose-107ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Holiday Hits",
                         "urls": ["https://lotus.stingray.com/manifest/ose-155ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Karaoke", "urls": [
                            "https://lotus.stingray.com/manifest/karaoke-kar000-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Naturescape", "urls": [
                            "https://lotus.stingray.com/manifest/naturescape-a003-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Nothin' But 90s",
                         "urls": ["https://lotus.stingray.com/manifest/ose-142ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Remember the 80s",
                         "urls": ["https://lotus.stingray.com/manifest/ose-128ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Rock Alternative",
                         "urls": ["https://lotus.stingray.com/manifest/ose-102ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Romance Latino",
                         "urls": ["https://lotus.stingray.com/manifest/ose-202ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Soul Storm",
                         "urls": ["https://lotus.stingray.com/manifest/ose-134ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray The Spa",
                         "urls": ["https://lotus.stingray.com/manifest/ose-122ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Today's KPOP",
                         "urls": ["https://lotus.stingray.com/manifest/ose-317ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Stingray Today's Latin Pop",
                         "urls": ["https://lotus.stingray.com/manifest/ose-190ads-montreal/samsungtvplus/master.m3u8"]},
                        {"name": "Swerve Sports",
                         "urls": ["https://linear-253.frequency.stream/dist/stirr/253/hls/master/playlist.m3u8",
                                  "https://linear-253.frequency.stream/dist/xumo/253/hls/master/playlist.m3u8"]},
                        {"name": "TD Ameritrade Network",
                         "urls": ["https://tdameritrade-distro.amagi.tv/playlist.m3u8"]}, {"name": "Telemundo West",
                                                                                           "urls": [
                                                                                               "https://content.uplynk.com/channel/b6a96ed39d694ae1b738faa98cf7dd3f.m3u8?delay=10800"]},
                        {"name": "The Archive",
                         "urls": ["https://stream-us-east-1.getpublica.com/playlist.m3u8?network_id=74"]},
                        {"name": "The Wall Street Journal Live",
                         "urls": ["https://d155hi8td9k2ns.cloudfront.net/out/wapo-medialive3-rtmp/live.m3u8"]},
                        {"name": "Toon Goggles",
                         "urls": ["https://weyyak-live.akamaized.net/weyyak_toon_goggles_ww/index.m3u8"]},
                        {"name": "Top Stories by Newsy",
                         "urls": ["https://content.uplynk.com/channel/33c48f602cfd4474b957eb4ad999caf8.m3u8"]},
                        {"name": "TVS Boxing", "urls": ["https://bozztv.com/gusa/gusa-tvsboxing/index.m3u8"]},
                        {"name": "TVS Cipher Network", "urls": ["https://bozztv.com/gusa/gusa-tvsmystery/index.m3u8"]},
                        {"name": "TVS Classic Movies",
                         "urls": ["https://bozztv.com/gusa/gusa-tvsclassicmovies/index.m3u8"]},
                        {"name": "TVS Drive In Movie",
                         "urls": ["https://bozztv.com/gusa/gusa-tvsdriveinmovie/index.m3u8"]},
                        {"name": "TVS Family Channel",
                         "urls": ["https://bozztv.com/gusa/gusa-TVSFamilyChannel/index.m3u8"]},
                        {"name": "TVS Flashback Network",
                         "urls": ["https://bozztv.com/gusa/gusa-TVSFlashback/index.m3u8"]},
                        {"name": "TVS Hollywood History",
                         "urls": ["https://bozztv.com/gusa/gusa-tvshollywoohistory/index.m3u8"]},
                        {"name": "TVS Mainstreet", "urls": ["https://bozztv.com/gusa/gusa-tvsmainst/index.m3u8"]},
                        {"name": "TVS Music Network", "urls": ["https://bozztv.com/gusa/gusa-tvsmusic/index.m3u8"]},
                        {"name": "TVS Nostalgia", "urls": ["https://bozztv.com/gusa/gusa-nostalgia/index.m3u8"]},
                        {"name": "TVS Nostalgia Movies",
                         "urls": ["https://bozztv.com/gusa/gusa-tvsNostalgiaMovies/index.m3u8"]},
                        {"name": "TVS Pinball Network",
                         "urls": ["https://bozztv.com/gusa/gusa-TVSCartoonNetwork/index.m3u8"]},
                        {"name": "TVS Quiz Show Network",
                         "urls": ["https://bozztv.com/gusa/gusa-tvsgameshow/index.m3u8"]},
                        {"name": "TVS Select Network", "urls": ["https://bozztv.com/gusa/gusa-tvsselect/index.m3u8"]},
                        {"name": "TVS Sports", "urls": ["https://bozztv.com/gusa/gusa-tvssports/index.m3u8"]},
                        {"name": "TVS Talk Network", "urls": ["https://bozztv.com/gusa/gusa-TVStalk/index.m3u8"]},
                        {"name": "TVS Tally Ho", "urls": ["https://bozztv.com/gusa/gusa-tvstallyho/index.m3u8"]},
                        {"name": "TVS Tavern", "urls": ["https://bozztv.com/gusa/gusa-tavern/index.m3u8"]},
                        {"name": "TVS Television Network", "urls": ["https://bozztv.com/gusa/gusa-tvstn/index.m3u8"]},
                        {"name": "TVS Travel Network", "urls": ["https://bozztv.com/gusa/gusa-tvstravel/index.m3u8"]},
                        {"name": "TVS Turbo", "urls": ["https://bozztv.com/gusa/gusa-tvsturbo/index.m3u8"]},
                        {"name": "TVS Vintage Network", "urls": ["https://bozztv.com/gusa/gusa-tvsvintage/index.m3u8"]},
                        {"name": "TVS Western Movie",
                         "urls": ["https://bozztv.com/gusa/gusa-tvswesternmovies/index.m3u8"]},
                        {"name": "WFTX News Fort Myers FL",
                         "urls": ["https://content.uplynk.com/channel/8975f81e6efd4e609873c3e8e25c7756.m3u8"]},
                        {"name": "White Plains Community Media",
                         "urls": ["https://stream.swagit.com/live-edge/whiteplainsny/smil:std-4x3-1-b/playlist.m3u8"]},
                        {"name": "WLS ABC 7 News (Chicago IL)", "urls": [
                            "https://content.uplynk.com/channel/ext/aac37e2c66614e699fb189ab391084ff/wls_24x7_news.m3u8"]},
                        {"name": "WTVD ABC 11 News (Raleigh-Durham NC)", "urls": [
                            "https://content.uplynk.com/channel/ext/f05837c508c44712aa7129d531f7dbe6/wtvd_24x7_news.m3u8"]},
                        {"name": "WXIA-DT1", "urls": ["https://livetv-fa.tubi.video/wxia/live.m3u8"]},
                        {"name": "XPTV1", "urls": ["https://janus.xpbroadcasting.com:8443/hls/xptv1.m3u8"]},
                        {"name": "Zoo Moo (Australia)",
                         "urls": ["https://cdn4.skygo.mn/live/disk1/Zoomoo/HLSv3-FTA/Zoomoo.m3u8"]}]
        }])

    def getName(self):
        return "🍥┃豆瓣┃🍥"

    def getCache(self, key):
        if key in self.cache.keys():
            return self.cache[key]
        return None

    def addCache(self, key, value):
        self.cache[key] = value

    def init(self, extend=""):
        self.init_logger()
        self.write_config({"douban_search": False}, "douban_search")
        self.logger.info("输出extend={}".format(extend))

    def Result(self, classes, vod_list, filters):
        result = {}
        result['class'] = classes
        result['list'] = vod_list
        if filters:
            result['filters'] = filters
        return result

    def addVodShort(self, data):
        nowData = data
        if self.getCache("vodShortData"):
            nowData.extend(self.getCache("vodShortData"))

        unique_data = []
        seen_ids = set()
        for item in nowData:
            item_id = item["vod_id"]
            if item_id not in seen_ids:
                unique_data.append(item)
                seen_ids.add(item_id)
        unique_data.reverse()
        self.addCache("vodShortData", unique_data)

    def getVodShort(self):
        return self.getCache("vodShortData")

    def homeContent(self, filter):
        classes = []
        config = {
            "@Bloomberg": "@UCIALMKvObZNtJ6AmdCLP7Lg",
            "@Hannah": "@UCJJylrYj79O88_ZnJ7-rZtw",
            "@SeekingTheNow": "@UCKRgtmruMpLghIraWWIBQ2A",
            "@KirinCamp": "@UC4zNtKX3XTplYQWNFPorvXQ",
            "#人工智能": "llm",
            "#徒步": "hiking",
            "#新闻": "news"
        }
        for tag in config.keys():
            classes.append({
                'type_name': tag,
                'type_id': config[tag]
            })
        if self.getVodShort():
            vod_list = self.getVodShort()
        else:
            vod_list = self.searchByKeyword("travel+vlog", 1)
            self.addVodShort(vod_list)
        filter = []
        return self.Result(classes, vod_list, filter)

    def homeVideoContent(self):
        pass

    def get_tags(self, extend):
        tag_list = []
        for key in list(extend.keys()):
            if key != "sort":
                tag_list.append(extend[key])
        return ",".join(tag_list)

    def categoryContent(self, tid, pg, filter, extend: dict):
        sort = extend["sort"] if "sort" in extend.keys() else "show_hot"
        tags = self.get_tags(extend)
        start_time = time.time()
        self.logger.info("tid:{},pg={},filter={},extend={}".format(tid, pg, filter, extend))
        start = (int(pg) - 1) * 20

        vod_list = self.searchByKeyword(tid, pg)
        result = {"jx": 0, "limit": 20, "page": int(pg), "pagecount": 2147483647, "parse": 0, "total": 2147483647}
        result["list"] = vod_list
        return result

    def getDetailInfoFromCache(self, id):
        if self.getCache(id + "_detail_info"):
            return self.getCache(id + "_detail_info")
        else:
            return None

    def putDetailInfoToCache(self, id, data):
        self.addCache(id + "_detail_info", data)

    def detailContent(self, array):
        vod_detail = VodDetail()
        VodData = self.getDetailInfoFromCache(array[0])
        if VodData is None:
            VodData = self.getVideoInfo(array[0])

        VodData = self.getVideoInfo(array[0])
        vod_detail.vod_name = VodData['name']
        vod_detail.vod_play_from = "Youtube"
        vod_detail.vod_play_url = VodData['vod_id']
        vod_detail.vod_director = VodData['channel_name']
        vod_detail.vod_content = VodData['content']
        vod_detail.type_name = VodData['type']
        vod_detail.vod_pic = VodData['vod_pic']
        vod_detail.vod_id = VodData['vod_id']

        self.addCache(vod_detail.vod_id, VodData)

        result = {
            'list': [
                vod_detail.to_dict()
            ]
        }
        return result

    def composeVodShorById(self, vodId):
        for n in self.getCache("vodShortData"):
            if n['vod_id'] == vodId:
                return [n]
        return None

    def searchContent(self, keyword, quick, key):
        if "##" in keyword:
            vodId = keyword.split("##")[-1]
            vod_short = self.composeVodShorById(vodId)
            if vod_short:
                return {"jx": 0, "parse": 0, "list": vod_short}
        vod_short = self.searchByKeyword(keyword, 1)
        self.addVodShort(vod_short)
        return {"jx": 0, "parse": 0, "list": vod_short}

    def playerContent(self, flag, id, vipFlags):
        vodInfo = self.getCache(id)
        format = "application/dash+xml"
        if vodInfo["url"].endswith("m3u8"):
            format = "application/vnd.apple.mpegurl"
        return {"url": "", "playUrl": vodInfo["url"], "parse": 0, "format": format}

    def convert_mpd_to_string(self, input_mpd_string):
        root = ET.fromstring(input_mpd_string)

        xmlDictData = self.xml_to_dict(root)
        audioList = []
        videoList = []
        mediaPresentationDuration = xmlDictData['@attributes']['mediaPresentationDuration']
        for info in xmlDictData['Period']['AdaptationSet']:
            if info['@attributes']['contentType'] == "audio":
                url = info['Representation']['BaseURL']['#text']
                indexRange = info['Representation']['SegmentBase']['@attributes']['indexRange']
                range = info['Representation']['SegmentBase']['Initialization']['@attributes']['range']
                bandwidth = info['Representation']['@attributes']['bandwidth']
                audioSamplingRate = info['Representation']['@attributes']['audioSamplingRate']
                codecs = info['Representation']['@attributes']['codecs']
                mimeType = info['@attributes']['mimeType']
                audioList.append({
                    "url": url,
                    "indexRange": indexRange,
                    "range": range,
                    "bandwidth": bandwidth,
                    "audioSamplingRate": audioSamplingRate,
                    "codecs": codecs,
                    "mimeType": mimeType
                })
            elif info['@attributes']['contentType'] == "video":
                mimeType = info['@attributes']['mimeType']
                frameRate = None
                if "frameRate" in info['@attributes'].keys():
                    frameRate = info['@attributes']['frameRate']
                for infoItem in info['Representation']:
                    url = infoItem['BaseURL']['#text']
                    if frameRate is None:
                        frameRate = infoItem['@attributes']['frameRate']
                    indexRange = infoItem['SegmentBase']['@attributes']['indexRange']
                    range = infoItem['SegmentBase']['Initialization']['@attributes']['range']
                    bandwidth = infoItem['@attributes']['bandwidth']
                    codecs = infoItem['@attributes']['codecs']
                    width = infoItem['@attributes']['width']
                    height = infoItem['@attributes']['height']
                    if int(width) <= 1920:
                        videoList.append({
                            "url": url,
                            "indexRange": indexRange,
                            "range": range,
                            "bandwidth": bandwidth,
                            "frameRate": frameRate,
                            "codecs": codecs,
                            "mimeType": mimeType,
                            "width": width,
                            "height": height
                        })
        return {"audioList": audioList, "videoList": videoList, "mediaPresentationDuration": mediaPresentationDuration}

    def isVideoFormat(self, url):
        pass

    def manualVideoCheck(self):
        return False

    def isVideoFormat(self):
        return True

    def localProxy(self, param):
        return [200, "video/MP2T", action, ""]

    def getPopular(self):
        host = self.page_url
        from pyquery import PyQuery as pq
        url = host + "/feed/popular"
        data = requests.get(url)
        d = pq(data.content)
        videos = []
        for item in d('.pure-g .pure-u-1.pure-u-md-1-4'):
            video_id = pq(item).find('.thumbnail a').attr('href').split('=')[-1]
            video_name = pq(item).find('.video-card-row a p').text()
            video_thumbnail = pq(item).find('.thumbnail img').attr('src')
            video_remarks = pq(item).find('.bottom-right-overlay .length').text() if pq(item).find(
                '.bottom-right-overlay .length').text() else ""
            videos.append({
                "vod_id": video_id,
                "vod_name": video_name + "##" + video_id,
                "vod_pic": host + video_thumbnail,
                "vod_remarks": video_remarks
            })
        return videos

    def getVideoInfo(self, vod_id):
        host = self.page_url
        from pyquery import PyQuery as pq
        url = host + "/watch?v=" + vod_id
        data = requests.get(url)
        d = pq(data.content)
        type = d('#genre').text().replace("Genre: ", "").strip()
        channel_name = d('#channel-name').text()
        content = d('#descriptionWrapper').text()
        name = d('#contents > div:nth-child(4) > h1').text()
        url = d("#player-container > video >source").attr("src")
        subtitle_url = host + "/api/v1/captions/" + vod_id + "?label=English (auto-generated)&type=.vtt"
        if url.startswith("http"):
            url = self.getVideoUrlByDash(url, subtitle_url)
        else:
            url = host + url
        vod_pic = host + d("#player-container > video").attr("poster")
        return {"name": name, "vod_id": vod_id, "content": content, "type": type, "channel_name": channel_name,
                "url": url, "vod_pic": vod_pic}

    def getVideoUrlByDash(self, url, subtitleUrl):
        xmlStr = self.session.get(url=url).text
        vodMetaInfo = self.convert_mpd_to_string(xmlStr.replace("&", "&amp;"))
        audioList = vodMetaInfo['audioList']
        videoList = vodMetaInfo['videoList']
        subtitleXml = ""
        subtitleResponse = self.session.get(subtitleUrl)
        if subtitleResponse.status_code == 200:
            subtitleXml = """
                    <AdaptationSet mimeType="text/vtt" lang="en" default="true">
                      <Representation id="{id}" bandwidth="256">
                        <BaseURL>{subtitle}</BaseURL>
                      </Representation>
                    </AdaptationSet>
                    """.format(subtitle=subtitleUrl.replace("&", "&amp;"), id=str(len(videoList) + len(audioList) + 1))
        mediaPresentationDuration = vodMetaInfo['mediaPresentationDuration']
        videoXmlList = []
        for i in range(len(videoList)):
            videoXmlList.append(
                """
            <AdaptationSet mimeType="{video_mimeType}" codecs="{video_codecs}" width="{video_width}" height="{video_height}" frameRate="{video_frameRate}">
                <Representation id="{id}" bandwidth="{video_bandwidth}">
                    <BaseURL>{video_url}</BaseURL>
                    <SegmentBase indexRange="{video_indexRange}">
                        <Initialization range="{video_range}"/>
                    </SegmentBase>
                </Representation>
            </AdaptationSet>
                """.format(
                    id=str(i),
                    video_mimeType=videoList[i]['mimeType'],
                    video_codecs=videoList[i]['codecs'],
                    video_bandwidth=videoList[i]['bandwidth'],
                    video_url=videoList[i]['url'].replace("&", "&amp;"),
                    video_indexRange=videoList[i]['indexRange'],
                    video_range=videoList[i]['range'],
                    video_frameRate=videoList[i]['frameRate'],
                    video_width=videoList[i]['width'],
                    video_height=videoList[i]['height'],
                )
            )

        audioXmlList = []
        for i in range(len(audioList)):
            audioXmlList.append(
                """
        <AdaptationSet mimeType="{audio_mimeType}" codecs="{audio_codecs}" lang="en">
            <Representation id="{id}" bandwidth="{audio_bandwidth}">
                <BaseURL>{audio_url}</BaseURL>
                <SegmentBase indexRange="{audio_indexRange}">
                    <Initialization range="{audio_range}"/>
                </SegmentBase>
            </Representation>
        </AdaptationSet>
                """.format(
                    id=str(i + len(videoList)),
                    audio_mimeType=audioList[0]['mimeType'],
                    audio_codecs=audioList[0]['codecs'],
                    audio_bandwidth=audioList[0]['bandwidth'],
                    audio_url=audioList[0]['url'].replace("&", "&amp;"),
                    audio_indexRange=audioList[0]['indexRange'],
                    audio_range=audioList[0]['range'],
                )
            )

        data = """<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd"
     type="static"
     profiles="urn:mpeg:dash:profile:isoff-on-demand:2011"
     minBufferTime="PT5S"
     mediaPresentationDuration="{duration}">
    <Period duration="{duration}">
        {videoXml}
        {audioXml}
        {subtitleXml}
    </Period>
</MPD>""".format(
            duration=mediaPresentationDuration,
            videoXml="\n".join(videoXmlList),
            audioXml="\n".join(audioXmlList),
            subtitleXml=subtitleXml
        )
        base64_encoded = base64.b64encode(data[len('<?xml version="1.0" encoding="utf-8"?>'):].encode("utf-8")).decode(
            'utf-8')
        # 构建 data URI
        data_uri = f"data:application/dash+xml;base64,{base64_encoded}"
        return data_uri

    def searchByKeyword(self, keyword, page):
        host = self.page_url
        from pyquery import PyQuery as pq
        if keyword.startswith("@"):
            keyword = keyword[1:]
            url = host + "/channel/" + keyword
        else:
            url = host + "/search?page=" + str(page) + "&q=" + keyword
        data = requests.get(url)
        d = pq(data.content)
        videos = []
        for item in d('.pure-g .pure-u-1.pure-u-md-1-4'):
            try:
                video_id = pq(item).find('.thumbnail a').attr('href').split('=')[-1]
                video_name = pq(item).find('.video-card-row a p').text()
                video_thumbnail = pq(item).find('.thumbnail img').attr('src')
                video_remarks = pq(item).find('.bottom-right-overlay .length').text() if pq(item).find(
                    '.bottom-right-overlay .length').text() else ""
                videos.append({
                    "vod_id": video_id,
                    "vod_name": video_name,
                    "vod_pic": host + video_thumbnail,
                    "vod_remarks": video_remarks
                })
            except:
                pass
        return videos
